package jssia.database.mysql.columns;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

abstract class BinaryColumn <T extends Serializable> extends Column {

    abstract long getMaxLength();

    public void set(PreparedStatement ps, int index, byte[] data) throws SQLException {
        if (data.length > getMaxLength())
            throw new IllegalArgumentException("Invalid length of " + data.length
                    + " bytes.  The Length must be less than " + getMaxLength());
        ps.setBytes(index, data);
    }

    public byte[] get(ResultSet rs) throws SQLException {
        return rs.getBytes(name);
    }

    public void setFileContents(PreparedStatement ps, int index, File file) throws SQLException, IOException {
        set(ps, index, getBytesFromFile(file));
    }

    public void getAndWriteToFile(ResultSet rs, File file) throws IOException, SQLException {
        writeBytesToFile(get(rs), file);
    }

    public void setObject(PreparedStatement ps, int index, T obj) throws SQLException, IOException {
        set(ps, index, serializeObjectToBytes(obj));
    }

    public T getObject(ResultSet rs) throws SQLException, IOException, ClassNotFoundException {
        return (T) deserializeBytesToObject(get(rs));
    }

    public static byte[] serializeObjectToBytes(Serializable obj) throws IOException {
        ObjectOutput out = null;
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            out = new ObjectOutputStream(bos);
            out.writeObject(obj);
            out.flush();
            return bos.toByteArray();
        } finally {
            if (out != null) out.close();
        }
    }

    public static Object deserializeBytesToObject(byte[] bytes) throws IOException, ClassNotFoundException {
        ObjectInputStream in = null;
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
            in = new ObjectInputStream(bis);
            return in.readObject();
        } finally {
            if (in != null) in.close();
        }
    }

    public static byte[] getBytesFromFile(File file) throws IOException {
        final long fileLength = file.length();
        if (fileLength > Integer.MAX_VALUE)
            throw new IllegalArgumentException("Currently can not support files larger than " + Integer.MAX_VALUE + " bytes." +
                    "  The file you are trying to serialize is " + fileLength + " bytes.");

        byte[] fileBytes = new byte[(int) fileLength];
        BufferedInputStream bis = null;
        try {
            bis = new BufferedInputStream(new FileInputStream(file));
            bis.read(fileBytes);
        } finally {
            if (bis != null) bis.close();
        }

        return fileBytes;
    }

    public static void writeBytesToFile(byte[] bytes, File file) throws IOException {
        OutputStream out = null;
        try {
            out = new BufferedOutputStream(new FileOutputStream(file));
            out.write(bytes);
        } finally {
            if (out != null) out.close();
        }
    }
}
